/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package pl.drypisy;

import java.awt.image.BufferedImage;

/**
 *
 * @author Adam Syrek
 */
public class Rotation3DProcess extends ImageProcess{

    private double d;
    double xAngle, yAngle, zAngle;
    double cosa, cosb, cosc, sina, sinb, sinc;
    int width, height;
    private BufferedImage src;
    int centerX, centerY;
    private double scale = 1.0;
    private double cosapow,cosbpow, coscpow, sinapow,sinbpow,sincpow;
    int xOffset, yOffset;

    //pozycja kamery
    private double cx=0, cy=0, cz=-100;

    //pozycja obserwatora
    private double ex=0, ey=0, ez=-300;

    public void setxAngle(double xAngle) {
        this.xAngle = xAngle;
    }

    public void setyAngle(double yAngle) {
        this.yAngle = yAngle;
    }

    public void setzAngle(double zAngle) {
        this.zAngle = zAngle;
    }
    
    public void setCameraZ(double cameraZ)
    {
        cz = -cameraZ;
    }
    
    public void setObserverZ(double observerZ)
    {
        ez = -observerZ;
    }

    public void setScale(double scale)
    {
        this.scale = scale;
    }
    
    @Override
    public BufferedImage process(BufferedImage img) {
        src=img;
        srcWidth = src.getWidth();
        srcHeight=src.getHeight();
        cosa = Math.cos(Math.toRadians(xAngle));
        cosb = Math.cos(Math.toRadians(yAngle));
        cosc = Math.cos(Math.toRadians(zAngle));
        sina = Math.sin(Math.toRadians(xAngle));
        sinb = Math.sin(Math.toRadians(yAngle));
        sinc = Math.sin(Math.toRadians(zAngle));
        cosapow = cosa*cosa;
        cosbpow = cosb*cosb;
        coscpow = cosc*cosc;
        sinapow = sina*sina;
        sinbpow = sinb*sinb;
        sincpow = sinc*sinc;
        width = (int)(srcWidth*scale);
        height = (int)(srcHeight*scale);
        centerX = width/2;
        centerY = height/2;
        //calcWidthAndHeight();
        cz=-300;
        //cz=-Math.sqrt(width*width + height*height)+ez;
        BufferedImage dest = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
        int[] outPixels = new int[width] ;
        int[] inPixels = src.getRGB(0, 0, srcWidth, srcHeight, null, 0, srcWidth);
        double out[] = new double[2];
        int nw, ne, sw, se;
        for (int y=0;y<height-1;y++) {
            for (int x=0;x<width-1;x++) {
                getOriginalCoordinates((int)(x /**scale*/), (int)(y/**scale*/),out);
                int srcX = (int)Math.floor( out[0] );
                int srcY = (int)Math.floor( out[1] );
                int pix;
                if(doInterpolation) {
                    double xWeight = out[0]-srcX;
                    double yWeight = out[1]-srcY;
                    nw = getPixel( inPixels, srcX, srcY, srcWidth, srcHeight);
                    ne = getPixel( inPixels, srcX+1, srcY, srcWidth, srcHeight );
                    sw = getPixel( inPixels, srcX, srcY+1, srcWidth, srcHeight );
                    se = getPixel( inPixels, srcX+1, srcY+1, srcWidth, srcHeight );
                    pix = bilinearInterpolation(xWeight, yWeight,  nw, ne, sw, se);
                }
                else pix = getPixel(inPixels,srcX,srcY,srcWidth,srcHeight);
                outPixels[x] = pix;
            }
            dest.setRGB(0, y, width, 1, outPixels, 0, height);
        }
        return dest;
    }

    @Override
    protected void getOriginalCoordinates(int x, int y, double[] out) {
        x=x-centerX;
        y=y-centerY;

//        double y1=((d)*(cosb*cosc*y + cosa*sinc*x - cosc*sina*sinb*x))/(cosb*d*(cosa*coscpow + cosa*sincpow));
//        double x1=((d)*(cosa*cosc*x - cosb*sinc*y + sina*sinb*sinc*x))/(cosb*d*(cosa*coscpow + cosa*sincpow));
        double x1 = (cx*cosapow*cosbpow*coscpow + cx*cosapow*cosbpow*sincpow + x*cosapow*cosb*cosc + cx*cosapow*coscpow*sinbpow + cx*cosapow*sinbpow*sincpow - y*cosa*cosbpow*sinc - y*cosa*sinbpow*sinc + cx*cosbpow*coscpow*sinapow + cx*cosbpow*sinapow*sincpow + x*cosb*cosc*sinapow + cx*coscpow*sinapow*sinbpow + y*cosc*sina*sinb + cx*sinapow*sinbpow*sincpow)/((coscpow + sincpow)*(cosapow*cosbpow + cosapow*sinbpow + cosbpow*sinapow + sinapow*sinbpow));
        double y1 = (cy*cosapow*cosbpow*coscpow + cy*cosapow*cosbpow*sincpow + x*cosapow*cosb*sinc + cy*cosapow*coscpow*sinbpow + cy*cosapow*sinbpow*sincpow + y*cosa*cosbpow*cosc + y*cosa*cosc*sinbpow + cy*cosbpow*coscpow*sinapow + cy*cosbpow*sinapow*sincpow + x*cosb*sinapow*sinc + cy*coscpow*sinapow*sinbpow + cy*sinapow*sinbpow*sincpow + y*sina*sinb*sinc)/((coscpow + sincpow)*(cosapow*cosbpow + cosapow*sinbpow + cosbpow*sinapow + sinapow*sinbpow));
        double z1 = (cz*cosapow*cosbpow + cz*cosapow*sinbpow - x*cosapow*sinb + cz*cosbpow*sinapow + y*cosb*sina + cz*sinapow*sinbpow - x*sinapow*sinb)/(cosapow*cosbpow + cosapow*sinbpow + cosbpow*sinapow + sinapow*sinbpow);
        x1 = (ex*z1 +ez*x1)/z1;
        y1 = (ey*z1 +ez*y1)/z1;

        out[0]=x1+centerX;
        out[1]=y1+centerY;
        //System.out.println(x1 + " " + y1);

    }

    private void calcWidthAndHeight()
    {
        //zle
        double rdx = (d*(cosb*cosc*srcWidth + cosb*sinc*srcHeight))/(d);
        double rdy = -(d*(srcWidth*(cosa*sinc - cosc*sina*sinb) - srcHeight*(cosa*cosc + sina*sinb*sinc)))/(d);
        double rux = (d*(cosb*cosc*srcWidth))/(d);
        double ruy = -(d*(srcWidth*(cosa*sinc - cosc*sina*sinb)))/(d);
        double ldx = (d*(cosb*sinc*srcHeight))/(d);
        double ldy = -(d*(-srcHeight*(cosa*cosc + sina*sinb*sinc)))/(d);
        double lux = (cosb*cosc*1.0 + cosb*sinc*1.0);
        double luy = -(d*(-1.0*(cosa*cosc + sina*sinb*sinc)))/(d);

        xOffset = (int)(lux+1);
        yOffset = (int)(luy+1);
        rdx = Math.abs(rdx);
        rdy = Math.abs(rdy);
        rux = Math.abs(rux);
        ruy = Math.abs(ruy);
        ldx = Math.abs(ldx);
        ldy = Math.abs(ldy);
        lux = Math.abs(lux);
        luy = Math.abs(luy);

//        width = (int)(Math.max(rdx,Math.max(rux,Math.max(ldx,lux)))+1);
//        height = (int)(Math.max(rdy,Math.max(ruy,Math.max(ldy,luy)))+1);
        //System.out.println(width + " " + height + "\n" + xOffset + " " + yOffset);
    }

//(cx*cosa^2*cosb^2*cosc^2 + cx*cosa^2*cosb^2*sinc^2 + cx*cosa^2*cosc^2*sinb^2 + cx*cosa^2*sinb^2*sinc^2 - cx*cosa*cosb^2*cosc - cy*cosa*cosb^2*sinc - cx*cosa*cosb*cosc^2 - cx*cosa*cosb*sinc^2 - cx*cosa*cosc*sinb^2 + cz*cosa*cosc*sinb - cy*cosa*sinb^2*sinc + cx*cosb^2*cosc^2*sina^2 + cx*cosb^2*sina^2*sinc^2 + cz*cosb^2*sina*sinc + cx*cosb*cosc + cy*cosb*sinc + cx*cosc^2*sina^2*sinb^2 + cy*cosc*sina*sinb + cx*sina^2*sinb^2*sinc^2 + cz*sina*sinb^2*sinc - cx*sina*sinb*sinc - cz*sinb)/(cosa^2*cosb^2*cosc^2 + cosa^2*cosb^2*sinc^2 - cosa^2*cosb*cosc + cosa^2*cosc^2*sinb^2 + cosa^2*sinb^2*sinc^2 - cosa*cosb^2*cosc - cosa*cosb*cosc^2 - cosa*cosb*sinc^2 + cosa*cosb - cosa*cosc*sinb^2 + cosa*cosc + cosb^2*cosc^2*sina^2 + cosb^2*sina^2*sinc^2 - cosb*cosc*sina^2 + cosb*cosc + cosc^2*sina^2*sinb^2 + sina^2*sinb^2*sinc^2 - 1)
//(cy*cosa^2*cosb^2*cosc^2 + cy*cosa^2*cosb^2*sinc^2 - cy*cosa^2*cosb*cosc + cx*cosa^2*cosb*sinc + cy*cosa^2*cosc^2*sinb^2 + cy*cosa^2*sinb^2*sinc^2 - cy*cosa*cosb*cosc^2 - cy*cosa*cosb*sinc^2 + cy*cosa*cosc + cz*cosa*sinb*sinc - cx*cosa*sinc + cy*cosb^2*cosc^2*sina^2 - cz*cosb^2*cosc*sina + cy*cosb^2*sina^2*sinc^2 - cy*cosb*cosc*sina^2 + cx*cosb*sina^2*sinc + cz*cosb*sina + cy*cosc^2*sina^2*sinb^2 - cz*cosc*sina*sinb^2 + cx*cosc*sina*sinb + cy*sina^2*sinb^2*sinc^2 + cy*sina*sinb*sinc)/(cosa^2*cosb^2*cosc^2 + cosa^2*cosb^2*sinc^2 - cosa^2*cosb*cosc + cosa^2*cosc^2*sinb^2 + cosa^2*sinb^2*sinc^2 - cosa*cosb^2*cosc - cosa*cosb*cosc^2 - cosa*cosb*sinc^2 + cosa*cosb - cosa*cosc*sinb^2 + cosa*cosc + cosb^2*cosc^2*sina^2 + cosb^2*sina^2*sinc^2 - cosb*cosc*sina^2 + cosb*cosc + cosc^2*sina^2*sinb^2 + sina^2*sinb^2*sinc^2 - 1)
//(cz*cosa^2*cosb^2*cosc^2 + cz*cosa^2*cosb^2*sinc^2 - cz*cosa^2*cosb*cosc + cz*cosa^2*cosc^2*sinb^2 - cx*cosa^2*cosc^2*sinb + cz*cosa^2*sinb^2*sinc^2 - cx*cosa^2*sinb*sinc^2 - cz*cosa*cosb^2*cosc + cz*cosa*cosb - cz*cosa*cosc*sinb^2 + cx*cosa*cosc*sinb + cy*cosa*sinb*sinc + cz*cosb^2*cosc^2*sina^2 + cz*cosb^2*sina^2*sinc^2 + cy*cosb*cosc^2*sina - cz*cosb*cosc*sina^2 + cy*cosb*sina*sinc^2 + cz*cosc^2*sina^2*sinb^2 - cx*cosc^2*sina^2*sinb - cy*cosc*sina + cz*sina^2*sinb^2*sinc^2 - cx*sina^2*sinb*sinc^2 - cz*sina*sinb*sinc + cx*sina*sinc)/(cosa^2*cosb^2*cosc^2 + cosa^2*cosb^2*sinc^2 - cosa^2*cosb*cosc + cosa^2*cosc^2*sinb^2 + cosa^2*sinb^2*sinc^2 - cosa*cosb^2*cosc - cosa*cosb*cosc^2 - cosa*cosb*sinc^2 + cosa*cosb - cosa*cosc*sinb^2 + cosa*cosc + cosb^2*cosc^2*sina^2 + cosb^2*sina^2*sinc^2 - cosb*cosc*sina^2 + cosb*cosc + cosc^2*sina^2*sinb^2 + sina^2*sinb^2*sinc^2 - 1)



}
